#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>
#include <math.h>
#include <time.h>


#define MAP_WIDTH 60
#define MAP_HEIGHT 20
#define INIT_SPEED 300


// 新增全局变量：用于存储居中偏移量
int offsetX = 0;
int offsetY = 0;

typedef struct {
    int x[100];
    int y[100];
    int len;
    int dir;
} Snake;

Snake snake;
COORD food = {0,0};
int score = 0;
int highest = 0;
int sleeptime = INIT_SPEED;
int gameover = 0;
int addscore = 1;

// 新增函数：计算居中偏移量
void CalculateCenterOffset() {
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
    int consoleWidth = csbi.srWindow.Right - csbi.srWindow.Left + 1;
    int consoleHeight = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
    
    offsetX = (consoleWidth - MAP_WIDTH) / 2;
    offsetY = (consoleHeight - MAP_HEIGHT) / 2;
    
    // 确保偏移量不为负
    if (offsetX < 0) offsetX = 0;
    if (offsetY < 0) offsetY = 0;
}

void HideCursor() {
    CONSOLE_CURSOR_INFO cursor_info = {1, 0};
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}

void SetConsoleCodePage() {
    SetConsoleOutputCP(65001);
    SetConsoleCP(65001);
}

void printsnake() {
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_INTENSITY);
    // 计算字符画的水平居中位置
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    GetConsoleScreenBufferInfo(hOut, &csbi);
    int consoleWidth = csbi.srWindow.Right - csbi.srWindow.Left + 1;
    int offset = (consoleWidth - 40) / 2; // 字符画宽度约为40字符
    
    for (int i = 0; i < offset; i++) printf(" ");
    printf("█████   █   █   █████   █   █   █████\n");
    
    for (int i = 0; i < offset; i++) printf(" ");
    printf("█       ██  █   █   █   █  █    █\n");
    
    for (int i = 0; i < offset; i++) printf(" ");
    printf("█████   █ █ █   █████   ███     ███\n");
    
    for (int i = 0; i < offset; i++) printf(" ");
    printf("    █   █  ██   █   █   █  █    █\n");
    
    for (int i = 0; i < offset; i++) printf(" ");
    printf("█████   █   █   █   █   █   █   █████\n");
    
    SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
}

void welcometogame() {
    system("cls");
    printsnake();
    
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    // 计算菜单的垂直居中位置
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    GetConsoleScreenBufferInfo(hOut, &csbi);
    int consoleHeight = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
    int startLine = (consoleHeight - 10) / 2; // 菜单大约占10行
    
    // 移动到菜单起始位置
    COORD pos = {0, (SHORT)(csbi.dwCursorPosition.Y + startLine)};
    SetConsoleCursorPosition(hOut, pos);
    
    SetConsoleTextAttribute(hOut, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
    printf("\n\n\t\t1. 开始游戏\n");
    printf("\t\t2. 查看最高分\n");
    printf("\t\t3. 游戏说明\n");
    printf("\t\t4. 退出游戏\n");
    SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
    printf("\t\t请输入选项(1-4): ");
}

// 修改地图绘制函数，添加偏移量
void createMap() {
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hOut, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
    
    // 上边框
    COORD pos = {(SHORT)offsetX, (SHORT)offsetY};
    SetConsoleCursorPosition(hOut, pos);
    for(int i=0; i<MAP_WIDTH; i++) printf("■");
    
    // 中间部分
    for(int i=1; i<MAP_HEIGHT-1; i++) {
        pos.Y = offsetY + i;
        pos.X = offsetX;
        SetConsoleCursorPosition(hOut, pos);
        printf("■");
        for(int j=1; j<MAP_WIDTH-1; j++) printf(" ");
        printf("■");
    }
    
    // 下边框
    pos.Y = offsetY + MAP_HEIGHT - 1;
    pos.X = offsetX;
    SetConsoleCursorPosition(hOut, pos);
    for(int i=0; i<MAP_WIDTH; i++) printf("■");
    
    SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
}

void File_out() {
    FILE *fp;
    fp = fopen("save.txt", "r");
    if(fp == NULL) {
        fp = fopen("save.txt", "w");
        fprintf(fp, "0");
        highest = 0;
    } else {
        fscanf(fp, "%d", &highest);
    }
    fclose(fp);
}

void initsnake() {
    snake.len = 3;
    snake.dir = 4;
    for(int i=0; i<snake.len; i++) {
        snake.x[i] = MAP_WIDTH/2 - i;
        snake.y[i] = MAP_HEIGHT/2;
    }
}

void createfood() {
    srand((unsigned)time(NULL));
    while(1) {
        food.X = rand() % (MAP_WIDTH-2) + 1;
        food.Y = rand() % (MAP_HEIGHT-2) + 1;
        int flag = 0;
        for(int i=0; i<snake.len; i++) {
            if(food.X == snake.x[i] && food.Y == snake.y[i]) {
                flag = 1;
                break;
            }
        }
        if(flag == 0) break;
    }
}

int biteself() {
    for(int i=1; i<snake.len; i++) {
        if(snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i]) {
            return 1;
        }
    }
    return 0;
}

void cantcrosswall() {
    if(snake.x[0] <= 0 || snake.x[0] >= MAP_WIDTH-1 || 
       snake.y[0] <= 0 || snake.y[0] >= MAP_HEIGHT-1) {
        gameover = 1;
    }
}

void snakemove() {
    for(int i=snake.len-1; i>0; i--) {
        snake.x[i] = snake.x[i-1];
        snake.y[i] = snake.y[i-1];
    }
    switch(snake.dir) {
        case 1: snake.y[0]--; break;
        case 2: snake.y[0]++; break;
        case 3: snake.x[0]--; break;
        case 4: snake.x[0]++; break;
    }
}

void speedup() {
    if(GetAsyncKeyState(VK_F1)&&sleeptime>10) {
        sleeptime -= 10;
        if(sleeptime==320) {
            addscore = 2;
        } else {
            addscore += 2;
        }
    }
}

void speeddown() {
    if(GetAsyncKeyState(VK_F2)&&sleeptime<320) {
        sleeptime += 30;
        if(sleeptime==350) {
            addscore=1;
        } else {
            addscore -=2;
        }
    }
}

void keyboardControl() {
    if(GetAsyncKeyState(VK_UP) && snake.dir != 2)    snake.dir = 1;
    else if(GetAsyncKeyState(VK_DOWN) && snake.dir != 1) snake.dir = 2;
    else if(GetAsyncKeyState(VK_LEFT) && snake.dir != 4) snake.dir = 3;
    else if(GetAsyncKeyState(VK_RIGHT) && snake.dir != 3) snake.dir = 4;
    else if(GetAsyncKeyState(VK_ESCAPE)) gameover = 1;
    speedup();
    speeddown();
}

// 游戏主流程函数
void startgame() {
    system("cls");  // 清空控制台界面
    // 计算当前控制台窗口的居中偏移量（用于内容居中显示）
    CalculateCenterOffset();
    
    File_out();      // 加载历史最高分记录
    createMap();     // 绘制游戏地图边框
    initsnake();     // 初始化蛇的初始位置和长度
    createfood();    // 生成第一个食物的位置
    HideCursor();    // 隐藏控制台光标（提升游戏显示效果）

    // 游戏主循环：只要未触发游戏结束条件就持续运行
    while(!gameover) {
        // 绘制食物（根据居中偏移量调整位置）
        COORD foodPos = {(SHORT)(food.X + offsetX), (SHORT)(food.Y + offsetY)};
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), foodPos);
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN);
        printf("●");

        // 绘制蛇身（每个蛇身节点根据居中偏移量调整位置）
        for(int i=0; i<snake.len; i++) {
            COORD pos = {(SHORT)(snake.x[i] + offsetX), (SHORT)(snake.y[i] + offsetY)};
            SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
            SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN);
            printf("★");
        }

        keyboardControl();  // 监听键盘输入（控制方向/加速/退出等）
        snakemove();        // 根据当前方向移动蛇的身体（头部先动，身体跟随）
        cantcrosswall();    // 检查蛇是否撞墙（触发游戏结束条件）
        if(biteself()) gameover = 1;  // 检查蛇是否咬到自己（触发游戏结束条件）

        // 处理吃食物逻辑：当蛇头坐标与食物坐标重合时
        if(snake.x[0] == food.X && snake.y[0] == food.Y) {
            sleeptime -= 10;    // 减少休眠时间（加快游戏速度）
            snake.len++;        // 蛇长度增加1格
            score += addscore;  // 当前得分增加（得分倍数由addscore决定）
            addscore += 2;      // 得分倍数增加2（后续吃食物得分更高）
            createfood();       // 生成新的食物位置（避免与蛇身重叠）
        }

        Sleep(sleeptime);       // 按当前速度暂停（控制游戏节奏）
        system("cls");          // 清屏以便重新绘制下一帧
        createMap();            // 重新绘制地图边框（保持界面稳定）
    }

    // 游戏结束后的处理流程
    system("cls");  // 清空旧界面
    CalculateCenterOffset();  // 重新计算居中偏移量（确保结束界面居中）
    
    // 设置光标位置到地图中心区域（显示游戏结束信息）
    COORD pos = {(SHORT)offsetX, (SHORT)(offsetY + MAP_HEIGHT/2 - 3)};
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
    printf("\n\n\t\t游戏结束！得分：%d\n", score);  // 显示当前得分
    
    // 比较并更新历史最高分
    if(score > highest) {
        printf("\t\t创纪录啦！最高分被刷新为：%d\n", score);
        FILE* fp = fopen("save.txt", "w");  // 保存新的最高分到文件
        fprintf(fp, "%d", score);
        fclose(fp);
    } else {
        printf("\t\t继续努力吧~ 离最高分还差：%d\n", highest - score);
    }
    printf("\t\t1.重玩\t2.退出\n");  // 显示重玩/退出选项
    
    // 清空键盘缓冲区（避免误触输入）
    while (_kbhit()) {
        _getch();
    }
    
    // 输入提示居中显示
    pos.Y += 5;  // 光标位置向下移动5行（调整提示位置）
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
    int choice;
    // 等待用户输入有效选项（1或2）
    do {
        printf("\t\t请输入选项(1-2): ");
        choice = _getch();
    } while(choice != '1' && choice != '2');
    
    // 根据用户选择处理：重玩则重置参数并重新开始游戏，否则退出
    if(choice == '1') {
        gameover = 0;    // 重置游戏结束标志
        score = 0;       // 重置当前得分
        sleeptime = INIT_SPEED;  // 重置初始速度
        startgame();     // 重新启动游戏主流程
    }
    else{
        return ;  // 退出游戏
    }
}
int main() {
    HideCursor();
    SetConsoleCodePage();
    while(1) {
        welcometogame();
        int choice = _getch();
        switch(choice) {
            case '1': startgame(); break;
            case '2': 
            {
                File_out();
                system("cls");
                CalculateCenterOffset();
                COORD pos = {(SHORT)offsetX, (SHORT)offsetY};
                SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
                printf("\n\t\t当前最高分：%d\n", highest); 
                printf("\t\t按任意键返回...");
                _getch(); 
                break;
            }
            case '3': 
            {
                system("cls");
                CalculateCenterOffset();
                COORD pos = {(SHORT)offsetX, (SHORT)offsetY};
                pos.X = offsetX;
                pos.Y = offsetY;
                SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
                printf("\n\t\t操作说明：方向键控制移动，F1加速，F2减速，ESC退出\n"); 
                printf("\t\t按任意键返回...");
                _getch();
                break;
            }
            case '4': return 0;
            default: 
            {
                system("cls");
                CalculateCenterOffset();
                COORD pos = {(SHORT)offsetX, (SHORT)offsetY};
                pos.X = offsetX;
                pos.Y = offsetY;
                SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
                printf("\n\t\t输入错误！按任意键返回...");
                _getch();
                break;
            }
        }
    }
    return 0;
}